package com.xjoyt.pay.master.dao.impl;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.persistence.Query;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;

import com.xjoyt.pay.abstracts.AbstractDao;
import com.xjoyt.pay.dto.PageParam;
import com.xjoyt.pay.dto.PageResult;
import com.xjoyt.pay.master.dao.OrderRepositoryCustom;
import com.xjoyt.pay.master.dao.predicate.OrderCommands;
import com.xjoyt.pay.master.params.MerchantOrderGroupParam;
import com.xjoyt.pay.master.params.OrderPageParam;
import com.xjoyt.pay.master.po.Order;
import com.xjoyt.pay.master.repository.OrderRepository;

public class OrderRepositoryImpl extends AbstractDao implements OrderRepositoryCustom {

	@Autowired
	private OrderRepository repository;

	@Override
	public PageResult<Order> loadPage(PageParam<OrderPageParam> param) {
		if (param.query != null)
			if (param.query.startDate != null && param.query.endDate != null) {
				param.query.startDate = toStartDate(param.query.startDate);
				param.query.endDate = toEndDate(param.query.endDate);
			}
		Pageable pageable = getPageable(param);
		Specification<Order> spec = getSpecification(OrderCommands.class, param.query);
		Page<Order> pageResult = repository.findAll(spec, pageable);

		return new PageResult<Order>(pageResult.getTotalElements(), pageResult.getContent());
	}

	@Override
	public Order findOneByMerchantIdAndOutTradeNo(String merchantId, String outTradeNo) {
		Page<Order> page = repository.findAllByMerchantIdAndOutTradeNo(merchantId, outTradeNo, getFirstPageable());
		return page.hasContent() ? page.iterator().next() : null;
	}

	@Override
	public Order findOneByNo(String no) {
		Page<Order> page = repository.findAllByNo(no, getFirstPageable());
		return page.hasContent() ? page.iterator().next() : null;
	}

	@Override
	public Order findOneByOutTradeNo(String outTradeNo) {
		Page<Order> page = repository.findAllByOutTradeNo(outTradeNo, getFirstPageable());
		return page.hasContent() ? page.iterator().next() : null;
	}

	@Override
	public Map<String, Object> sum(OrderPageParam param) {

		StringBuilder builder = new StringBuilder(
				" select new map(sum(a.money) as sum, count(*) as count) from Order a where a.id = a.id ");
		Map<String, Object> params = new HashMap<>();
		if (param.startDate != null && param.endDate != null) {
			param.startDate = toStartDate(param.startDate);
			param.endDate = toEndDate(param.endDate);
			builder.append(" and ( a.addedDate between :startDate and :endDate ) ");
			params.put("startDate", param.startDate);
			params.put("endDate", param.endDate);
		}

		if (param.success != null) {
			builder.append(" and ( a.success = :success) ");
			params.put("success", param.success);
		}

		if (param.done != null) {
			if (param.done.success == null) {
				builder.append(" and ( a.done = Null) ");
			} else {
				builder.append(" and ( a.done = :done) ");
				params.put("done", param.done.success);
			}

		}

		if (param.notify != null) {
			builder.append(" and ( a.notify = :notify) ");
			params.put("notify", param.notify);
		}

		if (param.payType != null) {
			builder.append(" and ( a.payType = :payType) ");
			params.put("payType", param.payType);
		}

		if (param.tradeType != null) {
			builder.append(" and ( a.tradeType = :tradeType) ");
			params.put("tradeType", param.tradeType);
		}

		if (param.check != null) {
			builder.append(" and ( a.check = :check) ");
			params.put("check", param.check);
		}

		if (param.back != null) {
			builder.append(" and ( a.back = :back) ");
			params.put("back", param.back);
		}

		if (param.pay != null) {
			if (param.pay.success == null) {
				builder.append(" and ( a.pay = Null) ");
			} else {
				builder.append(" and ( a.pay = :pay) ");
				params.put("pay", param.pay.success);
			}

		}

		if (StringUtils.isNotBlank(param.merchantId)) {
			builder.append(" and ( a.merchant.id=:merchantId ) ");
			params.put("merchantId", param.merchantId);
		}

		if (param.query != null) {
			builder.append(" and (a.no like '%" + param.query + "%' or a.merchant.no like '%" + param.query + "%')");
		}

		if (param.queryByChannel != null) {
			builder.append(" and ( a.orderConfig.channelNo like '%" + param.queryByChannel
					+ "%' or a.orderConfig.channelName like '%" + param.queryByChannel + "%')");
		}

		if (StringUtils.isNotBlank(param.distributorId)) {
			builder.append(" group by a.merchant.distributor.id ");
		}

		Query query = entityManager.createQuery(builder.toString());
		for (Entry<String, Object> entry : params.entrySet()) {
			query.setParameter(entry.getKey(), entry.getValue());
		}

		@SuppressWarnings("unchecked")
		Map<String, Object> result = (Map<String, Object>) query.getResultList().get(0);

		return result;
	}

	@Override
	public List<Map<String, Object>> merchantSum(List<String> merchantIds, MerchantOrderGroupParam param) {
		StringBuilder builder = new StringBuilder(
				" select new map(a.merchant.id as id, sum(a.money) as sum, count(*) as count)from Order a where a.merchant.id in :ids ");
		Map<String, Object> params = new HashMap<>();
		params.put("ids", merchantIds);

		if (param.startDate != null && param.endDate != null) {
			param.startDate = toStartDate(param.startDate);
			param.endDate = toEndDate(param.endDate);
			builder.append(" and ( a.addedDate between :startDate and :endDate ) ");
			params.put("startDate", param.startDate);
			params.put("endDate", param.endDate);
		}

		if (param.success != null) {
			builder.append(" and ( a.success = :success) ");

			params.put("success", param.success);
		}
		if (param.done != null) {
			builder.append(" and ( a.done = :done ) ");
			params.put("done", param.done);
		}

		builder.append(" group by a.merchant.id ");
		Query query = entityManager.createQuery(builder.toString());
		for (Entry<String, Object> entry : params.entrySet()) {
			query.setParameter(entry.getKey(), entry.getValue());
		}

		@SuppressWarnings("unchecked")
		List<Map<String, Object>> result = query.getResultList();
		return result;
	}

	@Override
	public List<Order> findAll(OrderPageParam param) {
		if (param.startDate != null && param.endDate != null) {
			param.startDate = toStartDate(param.startDate);
			param.endDate = toEndDate(param.endDate);
		}
		Specification<Order> spec = getSpecification(OrderCommands.class, param);
		List<Order> result = repository.findAll(spec);
		return result;
	}
}
